package com.strong.faker;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.RandomUtil;
import com.strong.FakerConstants;
import com.strong.enum_.GenderEnum;
import com.strong.enum_.ProvinceEnum;
import com.strong.model.IdNumber;
import com.strong.model.Place;
import com.strong.utils.FakerUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * 身份证 模拟数据生成类
 *
 * @author simen
 * @date 2023/04/07
 */
@Getter
@Slf4j
public class IdNumberFaker extends BaseFaker<IdNumber> {

    /**
     * 身份证中的日期格式
     */
    public static final String STR_ID_DATE_FORMAT = "yyyyMMdd";

    /**
     * 生成身份证允许的最小生日日期
     */
    private final static Date DATE_MIN_DATE_TIME = DateUtil.parseDate("1900-01-01");

    /**
     * 生成身份证允许的最小生日日期
     */
    private final static Long LONG_MIN_DATE_TIME = DATE_MIN_DATE_TIME.getTime();

    /**
     * 生成身份证允许的最大生日日期不能超过当前
     */
    private final static Date DATE_MAX_DATE_TIME = DateUtil.date();

    /**
     * 生成身份证允许的最大生日日期不能超过当前
     */
    private final static Long LONG_MAX_DATE_TIME = DATE_MAX_DATE_TIME.getTime();

    /**
     * 性别
     */
    private GenderEnum genderEnum;

    /**
     * 地址队列
     */
    private List<Place> listPlace;

    /**
     * 最小年偏移
     */
    private Integer intMinAnnualOffset;

    /**
     * 最大年偏移
     */
    private Integer intMaxAnnualOffset;

    /**
     * 注入生日
     *
     * @param intAgeMin 当前最小年龄
     * @param intAgeMax 当前最大年龄
     * @return {@link String}
     */
    public IdNumberFaker birthday(Integer intAgeMin, Integer intAgeMax) {
        // 断言最大年龄大于等于最小年龄
        Assert.isTrue(intAgeMax >= intAgeMin, "最大年龄{} 小于 最小年龄{}", intAgeMax, intAgeMin);

        DateTime dateNow = DateUtil.date();
        DateTime dateMin = DateUtil.offset(dateNow, DateField.YEAR, -1 * intAgeMax);
        DateTime dateMax = DateUtil.offset(dateNow, DateField.YEAR, -1 * intAgeMin);
        return birthday(dateMin, dateMax);
    }

    /**
     * 注入生日
     *
     * @param dateMin 最小日期
     * @param dateMax 最大日期
     * @return {@link IdNumberFaker}
     */
    public IdNumberFaker birthday(Date dateMin, Date dateMax) {
        // 断言非空
        Assert.isTrue(!ObjUtil.hasNull(dateMin, dateMax), "日期限制为空");
        // 断言最大日期大于最小日期
        Assert.isTrue(dateMax.getTime() >= dateMin.getTime(), "最大日期{} 应大于 最小日期{}", dateMax, dateMin);
        // 断言最小日期大于系统默认最小日期
        Assert.isTrue(dateMin.getTime() >= LONG_MIN_DATE_TIME, "最小日期{} 应大于系统默认最小日期", dateMin);
        // 断言最大日期小于系统默认最大日期
        Assert.isTrue(dateMax.getTime() <= LONG_MAX_DATE_TIME, "最大日期{} 应小于系统默认最大日期", dateMax);

        // 相对当前日期生成随机日期
        DateTime dateNow = DateUtil.date();
        intMinAnnualOffset = (int) DateUtil.between(dateNow, dateMin, DateUnit.DAY, false);
        intMaxAnnualOffset = (int) DateUtil.between(dateNow, dateMax, DateUnit.DAY, false);
        return this;
    }

    /**
     * 注入 所在地行政代码数组 只截取到县区级代码
     *
     * @param strsDivisionCode 所在地行政代码数组
     * @return {@link IdNumberFaker}
     */
    public IdNumberFaker divisionCode(String... strsDivisionCode) {
        // 断言参数非空
        Assert.isTrue(ArrayUtil.isNotEmpty(strsDivisionCode), "注入的所在地行政代码为空");

        // 从完整地址队列中找出对应的地址队列
        this.listPlace = FakerUtils.getPlaceList(strsDivisionCode);
        return this;
    }

    /**
     * 注入省枚举
     *
     * @param provinceEnums 省枚举
     * @return {@link IdNumberFaker}
     */
    public IdNumberFaker divisionCode(ProvinceEnum... provinceEnums) {
        // 断言参数非空
        Assert.isTrue(ArrayUtil.isNotEmpty(provinceEnums), "注入的省枚举数组为空");

        return divisionCode(ArrayUtil.addAll(Arrays.stream(provinceEnums).map(ProvinceEnum::getDivisionCode).toArray(String[][]::new)));
    }

    /**
     * 注入性别
     *
     * @param genderEnum 性别枚举
     * @return {@link IdNumberFaker}
     */
    public IdNumberFaker gender(GenderEnum genderEnum) {
        this.genderEnum = genderEnum;
        return this;
    }

    /**
     * 初始化
     */
    @Override
    protected void initialize() {
        // 判断地址队列是否为空 记录日志
        if (CollUtil.isEmpty(this.listPlace)) {
            // 如果为空，则赋值为完整地址队列
            listPlace = FakerConstants.LIST_ALL_PLACE;
            log.debug("地址编号数组[所有]");
        } else {
            StringBuilder sbLog = new StringBuilder();
            this.listPlace.forEach((place) -> sbLog.append(place.getIdPlaceCode()).append(","));
            log.debug("地址编号数组：{}", sbLog);
        }

        // 判断性别为空 记录日志
        if (ObjUtil.isNull(this.genderEnum)) {
            log.debug("性别[所有] ");
        } else {
            log.debug("性别：{}", this.genderEnum == GenderEnum.MALE ? "男" : "女");
        }

        // 如果出生年偏移有空，则注入初始值
        if (ObjUtil.hasNull(this.intMaxAnnualOffset, this.intMinAnnualOffset)) {
            birthday(DATE_MIN_DATE_TIME, DATE_MAX_DATE_TIME);
        }
        log.debug("年龄段：{}-{}岁之间", -this.intMaxAnnualOffset / 365, -this.intMinAnnualOffset / 365);
    }

    @Override
    protected IdNumber getModel() {
        // 获取随机生日
        Date birthday = RandomUtil.randomDate(DateUtil.date(), DateField.DAY_OF_YEAR, intMinAnnualOffset, intMaxAnnualOffset);
        // 获取随机地址对象
        Place place = RandomUtil.randomEle(this.listPlace);
        // 获取随机性别
        GenderEnum ge = ObjUtil.isNotNull(this.genderEnum) ?
                this.genderEnum : RandomUtil.randomBoolean() ? GenderEnum.FEMALE : GenderEnum.MALE;
        return new IdNumber(place, ge, birthday);
    }
}
